package com.lastproject.ejb.dao;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.servlet.ServletContext;
import javax.xml.bind.DatatypeConverter;

import com.lastproject.dto.ConfidentialEmployeeDTO;
import com.lastproject.ejb.entities.ConfidentialEmployee;
import com.lastproject.ejb.entities.Department;
import com.lastproject.ejb.entities.Employee;
import com.lastproject.ejb.entities.Role;
import com.lastproject.ejb.entities.User;
import com.lastproject.ejb.service.MailSender;
import com.lastproject.ejb.service.PasswordEncryption;
import com.lastproject.ejb.util.ConfidentialEmployeeConversion;

/**
 * Session Bean implementation class ConfidentialEmployeeDAO
 */
@Stateless
public class ConfidentialEmployeeDAO implements ConfidentialEmployeeDAORemote {

	@PersistenceContext
	EntityManager entityManager;

	@EJB
	ConfidentialEmployeeConversion conv;

	public ConfidentialEmployeeDAO() {

	}

	@Override
	public void create(ConfidentialEmployeeDTO ceDTO) throws Exception {
		System.out.println("create with mail");
		// initial password, represented as a string
		String tempPass = ceDTO.getPassword();
		String password_encrypted2 = "";

		try {

			// initial password after being hashed using SHA-1
			// represented as byte[]
			byte[] hashedPassword = new PasswordEncryption()
					.getEncryptedPassword(tempPass,
							new PasswordEncryption().getSalt());

			// hashed password stored as string
			// this version of the password will be stored in the db
			password_encrypted2 = DatatypeConverter
					.printHexBinary(hashedPassword);
			ceDTO.setPassword(password_encrypted2);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException
				| UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		User auxCE = conv.fromDTO(ceDTO);
		List<Role> roles = conv.convertRolesFromDTO(ceDTO);
		
		// set user for roles
		for (int i = 0; i < roles.size(); i++) {
			List<User> userListForRole = new ArrayList<>();
			User myUser = auxCE;
			userListForRole.add(myUser);
		}

		// set roles for user
		auxCE.setRoles(roles);
		try {
		entityManager.persist(auxCE);

		// send email with instructions to activate account
		sendInstructionsEmail("newAccount", ceDTO.getWorkEmail(),
				ceDTO.getUsername(), password_encrypted2);
		} catch (Exception e) {
			throw new Exception(e);
		}

	}

	@Override
	public ConfidentialEmployeeDTO findById(int id) {
		// System.out.println("in dao, findById, id: " + id);
		ConfidentialEmployee ce = entityManager.find(
				ConfidentialEmployee.class, id);

		if (ce != null) {

			ConfidentialEmployeeDTO cedto = conv.fromEntity(ce);

			List<Integer> rolesID = conv.convertRolesFromEntity(ce);
			cedto.setRolesIdList(rolesID);

			return cedto;
		} else {
			return null;
		}

	}

	@Override
	public List<ConfidentialEmployeeDTO> getAll() {
		List<ConfidentialEmployee> ceList = entityManager.createQuery(
				"Select ce from ConfidentialEmployee ce",
				ConfidentialEmployee.class).getResultList();

		List<ConfidentialEmployeeDTO> confEmployeesDTO = new ArrayList<ConfidentialEmployeeDTO>();

		for (ConfidentialEmployee ce : ceList) {
			ConfidentialEmployeeDTO uDTO = new ConfidentialEmployeeDTO();
			uDTO = conv.fromEntity(ce);
			confEmployeesDTO.add(uDTO);

		}

		return confEmployeesDTO;
	}

	@Override
	public void edit(ConfidentialEmployeeDTO ceDTO) {

		User auxCE = conv.fromDTO(ceDTO);
		List<Role> roles = conv.convertRolesFromDTO(ceDTO);

		// set user for roles
		for (int i = 0; i < 6; i++) {
			List<User> userListForRole = new ArrayList<>();
			User myUser = null;
			userListForRole.add(myUser);
		}

		// set roles for user
		auxCE.setRoles(null);
		// auxCE.addRoles(r);

		entityManager.merge(auxCE);
		entityManager.flush();

		for (int i = 0; i < roles.size(); i++) {
			List<User> userListForRole = new ArrayList<>();
			User myUser = auxCE;
			userListForRole.add(myUser);
		}
		auxCE.setRoles(roles);
		entityManager.merge(auxCE);
		entityManager.flush();

	}

	@Override
	public void remove(ConfidentialEmployeeDTO ceDTO) {
		ConfidentialEmployee confEmp = entityManager.find(
				ConfidentialEmployee.class, conv.fromDTO(ceDTO).getId());
		entityManager.remove(confEmp);
	}

	public void sendInstructionsEmail(String emailPurpose,
			String receiverEmail, String receiverUsername, String encryptPass) {
		MailSender ms = new MailSender();

		String emailSender = "no-reply@msg-systems.com";
		String emailSubject = "";
		String emailContent = "";

		// property file that contains ip address of server
		// on which project is run
		// at the moment - localhost
		Properties ipConfigFile = new Properties();
		// property file is stored in META-INF folder
		InputStream myIS = getClass().getClassLoader().getResourceAsStream(
				"META-INF/ipconfigs.properties");

		try {
			// open property file
			ipConfigFile.load(myIS);
		} catch (IOException e) {
			e.printStackTrace();
		}

		ServletContext servletContext = (ServletContext) FacesContext
			    .getCurrentInstance().getExternalContext().getContext();
		// get value of "ip" property
		String ipAddress = ipConfigFile.getProperty("ip");
		String firstAccessLink = ipAddress
				+ servletContext.getServletContextName() + "/faces/changePassword.xhtml" + "?logUsername="
				+ receiverUsername + "&logPassword=" + encryptPass;

		switch (emailPurpose) {

		case "newAccount":
			emailSubject = "Your account was created";
			emailContent = "Click the link provided below and follow the instructions"
					+ " to activate your account.\n\n" + firstAccessLink;
			break;
		}

		ms.sendMail(emailSender, receiverEmail, emailSubject, emailContent);
	}

	@Override
	public List<ConfidentialEmployeeDTO> findByCriteria(String name,
			String position, String employmentType, Date startEmploymentDate,
			Date endEmploymentDate, Date startLastMedCheckDate,
			Date endLastMedCheckDate, boolean enabledAccount, int roleId,
			int departmentId, String germanContactPers, String careerLevel,
			float startSalary, float endSalary) {

		List<ConfidentialEmployee> results = new ArrayList<ConfidentialEmployee>();

		String selectPart = "SELECT DISTINCT ce FROM ConfidentialEmployee ce";
		StringBuilder wherePart = new StringBuilder();
		wherePart.append(" WHERE ");

		// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

		boolean previous = false;

		if (!name.isEmpty()) {
			wherePart.append("(" + "(ce.firstname LIKE '%" + name + "%') "
					+ "or (ce.lastname LIKE '%" + name + "%') "
					+ "or (concat(ce.firstname, ' ', ce.lastname) = '" + name
					+ "') " + "or (concat(ce.lastname, ' ', ce.firstname) = '"
					+ name + "')" + ")");
			previous = true;
		}

		if (!position.isEmpty()) {
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.position = '" + position + "')");
			previous = true;
		}

		try {
			if (!employmentType.isEmpty()) {
				if (previous) {
					wherePart.append(" AND ");
				}
				wherePart.append("(ce.employmentType = '" + employmentType
						+ "')");
				previous = true;
			}
		} catch (NullPointerException e) {

		}

		if ((startEmploymentDate != null) && (endEmploymentDate != null)
				&& (startEmploymentDate.compareTo(endEmploymentDate) < 0)) {
			// between start and end
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.employmentDate BETWEEN '"
					+ new java.sql.Date(startEmploymentDate.getTime())
					+ "' AND '"
					+ new java.sql.Date(endEmploymentDate.getTime()) + "')");
			previous = true;
		} else if (startEmploymentDate != null && (endEmploymentDate == null)) {
			// from start onwards
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.employmentDate > '"
					+ new java.sql.Date(startEmploymentDate.getTime()) + "')");
			previous = true;
		}

		try {
			if ((startLastMedCheckDate != null)
					&& (endLastMedCheckDate != null)
					&& (startLastMedCheckDate.compareTo(endLastMedCheckDate) < 0)) {
				// between start and end
				if (previous) {
					wherePart.append(" AND ");
				}
				wherePart.append("(ce.medCheckUpDate BETWEEN '"
						+ new java.sql.Date(startLastMedCheckDate.getTime())
						+ "' AND '"
						+ new java.sql.Date(endLastMedCheckDate.getTime())
						+ "')");
				previous = true;
			} else if (startLastMedCheckDate != null
					&& (endLastMedCheckDate == null)) {
				// from start onwards
				if (previous) {
					wherePart.append(" AND ");
				}
				wherePart.append("(ce.medCheckUpDate > '"
						+ new java.sql.Date(startLastMedCheckDate.getTime())
						+ "')");
				previous = true;
			}
		} catch (NullPointerException e) {

		}

		// department
		if (departmentId != 0) {
			if (previous) {
				wherePart.append(" AND ");
			}

			wherePart.append("(ce.department.id = " + departmentId + ")");
			previous = true;
		}

		try {
			if (enabledAccount == true) {
				if (previous) {
					wherePart.append(" AND ");
				}
				wherePart.append("(ce.enabled = " + 1 + ")");
				previous = true;
			} else {
				if (previous) {
					wherePart.append(" AND ");
				}
				wherePart.append("(ce.enabled = " + 0 + ")");
				previous = true;
			}
		} catch (Exception e) {

		}

		if (!germanContactPers.isEmpty()) {
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.germanContactPerson = '" + germanContactPers
					+ "')");
			previous = true;
		}

		try {
			if (!careerLevel.isEmpty()) {
				if (previous) {
					wherePart.append(" AND ");
				}
				wherePart.append("(ce.careerLevel = '" + careerLevel + "')");
				previous = true;
			}
		} catch (NullPointerException e) {

		}

		if ((startSalary == endSalary) && startSalary != 0) {
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.salary = " + startSalary + ")");
			previous = true;
		} else if (startSalary != 0 && endSalary != 0) {
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.salary BETWEEN " + startSalary + " AND "
					+ endSalary + ")");
			previous = true;
		} else if (startSalary != 0 && endSalary == 0) {
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.salary > " + startSalary + ")");
			previous = true;
		} else if (startSalary == 0 && endSalary != 0) {
			if (previous) {
				wherePart.append(" AND ");
			}
			wherePart.append("(ce.salary < " + endSalary + ")");
			previous = true;
		}

		String fullQuery = selectPart + wherePart.toString();

		results = entityManager.createQuery(fullQuery,
				ConfidentialEmployee.class).getResultList();

		List<ConfidentialEmployeeDTO> resultsDTO = new ArrayList<ConfidentialEmployeeDTO>();

		for (ConfidentialEmployee ce : results) {
			resultsDTO.add(conv.fromEntity(ce));
		}

		return resultsDTO;

	}

	@Override
	public boolean checkIfDeparmentHasManager(int departmentId) {
		// employee - isDepartmentManager
		// employee - deparmentID
		Department department = entityManager.find(Department.class,
				departmentId);

		List<Employee> listOfDepartmentManagers = entityManager
				.createQuery(
						"SELECT ce FROM Employee ce JOIN ce.department d "
								+ "WHERE ce.isDepartmentManager = '1' "
								+ "AND d.id = :department", Employee.class)
				.setParameter("department", departmentId).getResultList();

		if (listOfDepartmentManagers.size() > 0) {
			// department has manager
			return true;
		} else
			// department doesn't have manager
			return false;
	}

	@Override
	public ConfidentialEmployeeDTO findByWorkemail(String workemail) {
		List<ConfidentialEmployee> ceList = new ArrayList<>();

		ceList = entityManager
				.createQuery(
						"SELECT ce FROM ConfidentialEmployee ce "
								+ "WHERE ce.workEmail = :workemail",
						ConfidentialEmployee.class)
				.setParameter("workemail", workemail).getResultList();

		if (ceList.size() != 0) {
			ConfidentialEmployeeDTO cedto = conv.fromEntity(ceList.get(0));
			List<Integer> rolesID = conv.convertRolesFromEntity(ceList.get(0));
			cedto.setRolesIdList(rolesID);

			return cedto;
		} else {
			return null;
		}
	}

	@Override
	public boolean existsUsername(String username) {
		try {
			List<ConfidentialEmployee> emp = entityManager
					.createQuery(
							"SELECT ce FROM ConfidentialEmployee ce WHERE ce.username=:username",
							ConfidentialEmployee.class)
					.setParameter("username", username).getResultList();
			if (emp.size() == 0)
				return false;
			else
				return true;
		} catch (NoResultException e) {
			return true;
		}
	}

	@Override
	public void createNoEmailSend(ConfidentialEmployeeDTO ceDTO)
			throws Exception {
		// TODO Auto-generated method stub

		// initial password, represented as a string
		String tempPass = ceDTO.getPassword();
		String password_encrypted2 = "";

		try {

			// initial password after being hashed using SHA-1
			// represented as byte[]
			byte[] hashedPassword = new PasswordEncryption()
					.getEncryptedPassword(tempPass,
							new PasswordEncryption().getSalt());

			// hashed password stored as string
			// this version of the password will be stored in the db
			password_encrypted2 = DatatypeConverter
					.printHexBinary(hashedPassword);
			ceDTO.setPassword(password_encrypted2);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException
				| UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		User auxCE = conv.fromDTO(ceDTO);
		List<Role> roles = conv.convertRolesFromDTO(ceDTO);

		// set user for roles
		for (int i = 0; i < roles.size(); i++) {
			List<User> userListForRole = new ArrayList<>();
			User myUser = auxCE;
			userListForRole.add(myUser);
		}

		// set roles for user
		auxCE.setRoles(roles);
		try {
		entityManager.persist(auxCE);
		
		} catch (Exception e) {
			throw new Exception(e);
		}

	}

}
